#include "helper.h"
#include "monitor.h"
#include "reg.h"

extern uint32_t instr;
extern char assembly[80];
extern char trace_buf[128];

/* decode R-type instrucion */
static void decode_r_type(uint32_t instr)
{

	op_src1->type = OP_TYPE_REG;// R型寄存器的操作数均为寄存器
	op_src1->reg = (instr & RS_MASK) >> (RT_SIZE + IMM_SIZE);// 取出指令中rs寄存器编号
	op_src1->val = reg_w(op_src1->reg);// 取出寄存器中的值
	
	op_src2->type = OP_TYPE_REG;
	op_src2->reg = (instr & RT_MASK) >> (RD_SIZE + SHAMT_SIZE + FUNC_SIZE);// 取出rt寄存器编号
	op_src2->val = reg_w(op_src2->reg);

	op_dest->type = OP_TYPE_REG;
	op_dest->reg = (instr & RD_MASK) >> (SHAMT_SIZE + FUNC_SIZE);
}

make_helper(add){
	decode_r_type(instr);
	int sign_v1 = op_src1->val; 
	int sign_v2 = op_src2->val;
	int temp = sign_v1+sign_v2;
	int temp1 = sign_v1&0x80000000; 
	int temp2 = sign_v2&0x80000000;
	if(((temp&0x80000000)!=temp1)&&(temp1==temp2)){
		//Exception 
		if(cpu.cp0.status.EXL==0){
			cpu.cp0.cause.ExcCode = Ov;
			cpu.cp0.epc = cpu.pc;
			cpu.pc = Trap_addr - 4;
			cpu.cp0.status.EXL = 1;
		}
		reg_w(op_dest->reg) = temp;
	}else{
		reg_w(op_dest->reg) = temp;
	}
	sprintf(assembly, "add   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(and) {
	decode_r_type(instr);
	reg_w(op_dest->reg) = (op_src1->val & op_src2->val);
	sprintf(assembly, "and   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
	
}

make_helper(addu){
	decode_r_type(instr);
	reg_w(op_dest->reg) = (op_src1->val+op_src2->val);
	sprintf(assembly, "addu   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(sub){
	decode_r_type(instr);
	int sign_v1 = op_src1->val; 
	int sign_v2 = op_src2->val;
	int temp = sign_v1-sign_v2;
	int temp1 = sign_v1&0x80000000; 
	int temp2 = sign_v2&0x80000000;
	if(((temp&0x80000000)!=temp1)&&(temp1!=temp2)){
		//Overflow Exception 
		if(cpu.cp0.status.EXL==0){
			cpu.cp0.cause.ExcCode = Ov;
			cpu.cp0.epc = cpu.pc;
			cpu.pc = Trap_addr - 4;
			cpu.cp0.status.EXL = 1;
		}
		reg_w(op_dest->reg) = temp;
	}else{
		reg_w(op_dest->reg) = temp;
	}
	sprintf(assembly, "sub   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(subu){
	decode_r_type(instr);
	reg_w(op_dest->reg) = (op_src1->val-op_src2->val);
	sprintf(assembly, "subu   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(slt){
	decode_r_type(instr);
	int temp1 = op_src1->val; 
	int temp2 = op_src2->val;
	reg_w(op_dest->reg) = temp1>temp2 ? 0 : 1;
	sprintf(assembly, "slt   %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(sltu){
	decode_r_type(instr);
	reg_w(op_dest->reg) = op_src1->val>op_src2->val ? 0 : 1;
	sprintf(assembly, "sltu  %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(div){
	decode_r_type(instr);
	int temp1 = op_src1->val; 
	int temp2 = op_src2->val;
	if(temp2==0){
		//Exception
		if(cpu.cp0.status.EXL==0){
			cpu.cp0.cause.ExcCode = Ov;
			cpu.cp0.epc = cpu.pc;
			cpu.pc = Trap_addr - 4;
			cpu.cp0.status.EXL = 1;
		}
	} else {
		cpu.hi = temp1 % temp2;
		cpu.lo = temp1/temp2;
	}
	sprintf(assembly, "div   %s,   %s",  REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(divu){
	decode_r_type(instr);
	if (op_src2->val == 0) {
		//Exception
		if(cpu.cp0.status.EXL==0){
			cpu.cp0.cause.ExcCode = Ov;
			cpu.cp0.epc = cpu.pc;
			cpu.pc = Trap_addr - 4;
			cpu.cp0.status.EXL = 1;
		}
	} else {
		cpu.hi = op_src1->val%op_src2->val;
		cpu.lo = op_src1->val/op_src2->val;
	}
	sprintf(assembly, "div   %s,   %s",  REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(mult){
	decode_r_type(instr);
	long long temp1, temp2;
	if(((int)op_src1->val)<0) 
		temp1 = (0xFFFFFFFFL<<32)|op_src1->val;
	else 
		temp1 = op_src1->val;
	if(((int)op_src2->val)<0) 
		temp2 = (0xFFFFFFFFL<<32)|op_src2->val;
	else 
		temp2 = op_src2->val;
	uint64_t outcome = temp1*temp2;
	uint32_t lo = outcome&0xFFFFFFFF;
	uint32_t hi = (outcome>>32)&0xFFFFFFFF;
	cpu.lo = lo; 
	cpu.hi = hi;
	// TODO : realize this oprand.
	// int temp1 = (op_src1->val)&0x0000ffff; int temp2 = (op_src2->val)&0x0000ffff;
	sprintf(assembly, "mult   %s,   %s",  REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(multu){
	decode_r_type(instr);
	uint64_t temp1 = op_src1->val; 
	uint64_t temp2 = op_src2->val;
	uint64_t outcome = temp1*temp2;
	uint32_t lo = outcome&0xFFFFFFFF;
	uint32_t hi = (outcome>>32)&0xFFFFFFFF;
	cpu.lo = lo; 
	cpu.hi = hi;
	sprintf(assembly, "mult u  %s,   %s",  REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(nor){
	decode_r_type(instr);
	reg_w(op_dest->reg) = ((op_src1->val)|(op_src2->val))^(0xffffffff);
	sprintf(assembly, "nor  %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(or){
	decode_r_type(instr);
	reg_w(op_dest->reg) = (op_src1->val)|(op_src2->val);
	sprintf(assembly, "or  %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(xor){
	decode_r_type(instr);
	reg_w(op_dest->reg) = (op_src1->val)^(op_src2->val);
	sprintf(assembly, "xor  %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(sllv){
	decode_r_type(instr);
	int temp = (op_src1->val)&0x1F;
	uint32_t temp2 = op_src2->val;
	uint32_t outcome = temp2<<temp;
	reg_w(op_dest->reg) = outcome;
	sprintf(assembly, "sllv  %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(sll){
	decode_r_type(instr);
	int sa = (instr&SHAMT_MASK)>>FUNC_SIZE; //A little different.
	reg_w(op_dest->reg) = (op_src2->val)<<sa;
	sprintf(assembly, "sll  %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(srav){
	decode_r_type(instr);
	uint32_t temp = (op_src1->val)&0x1F;
	int temp2 = op_src2->val;
	reg_w(op_dest->reg) = (temp2)>>temp;
	sprintf(assembly, "srav  %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(sra){
	decode_r_type(instr);
	int sa = (instr&SHAMT_MASK)>>FUNC_SIZE; //A little different.
	int temp = op_src2->val;
	reg_w(op_dest->reg) = (temp)>>sa;
	sprintf(assembly, "sra  %s,   %s,   0x%04x", REG_NAME(op_dest->reg), REG_NAME(op_src2->reg), sa);
}

make_helper(srlv){
	decode_r_type(instr);
	uint32_t temp = (op_src1->val)&0x1F;
	reg_w(op_dest->reg) = (op_src2->val)>>(temp);
	sprintf(assembly, "srlv  %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

make_helper(srl){
	decode_r_type(instr);
	int sa=(instr&SHAMT_MASK)>>FUNC_SIZE; //A little different.
	uint32_t temp=op_src2->val;
	reg_w(op_dest->reg)=(temp)>>sa;
	sprintf(assembly, "srl  %s,   %s,   %s", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), REG_NAME(op_src2->reg));
}

// MOVE
// guorui 2024.10.19
make_helper(mfhi)
{
	uint32_t rd = (instr >> 11) & 0x1F;
	reg_w(rd) = cpu.hi;
	sprintf(assembly, "mfhi  %s", REG_NAME(rd));
}

// guorui 2024.10.19
make_helper(mflo)
{
	uint32_t rd = (instr >> 11) & 0x1F;
	reg_w(rd) = cpu.lo;
	sprintf(assembly, "mflo  %s", REG_NAME(rd));
}

// guorui 2024.10.19
make_helper(mthi)
{
	decode_r_type(instr);
	uint32_t temp = op_src1->val;
	cpu.hi = temp;
	sprintf(assembly, "mthi  %s", REG_NAME(op_src1->reg));
}

// guorui 2024.10.19
make_helper(mtlo)
{
	decode_r_type(instr);
	uint32_t temp = op_src1->val;
	cpu.lo = temp;
	sprintf(assembly, "mtlo  %s", REG_NAME(op_src1->reg));
}

// guorui 2024.10.19
make_helper(jr) {
    decode_r_type(instr);
    cpu.pc=(((int)cpu.pc)&0xFFFF0000) | (op_src1->val);
    cpu.pc-=4;
    sprintf(assembly, "jr %s", REG_NAME(op_src1->reg));
}

// guorui 2024.10.19
make_helper(jalr) {
    decode_r_type(instr);
    reg_w(op_dest->reg)=cpu.pc+8;
    cpu.pc=(((int)cpu.pc)&0xFFFF0000) | (op_src1->val);
    cpu.pc-=4;
    sprintf(assembly, "jr %s", REG_NAME(op_src1->reg));
}

// break
make_helper(break_)
{
	cpu.cp0.cause.ExcCode=Bp;
	cpu.cp0.status.EXL=1;
	cpu.cp0.epc=cpu.pc;
	cpu.pc=Trap_addr - 4;
	sprintf(assembly, "break");
}

// syscall
make_helper(syscall)
{
	cpu.cp0.cause.ExcCode = Sys;
	cpu.cp0.status.EXL = 1;
	cpu.cp0.epc = cpu.pc;
	cpu.pc = Trap_addr - 4;
	sprintf(assembly, "syscall");
}